<?php

class Social_Post extends Social_Base
{
    const TYPE_BLOG = 'blog';
    const TYPE_WIKI = 'wiki';
    const TYPE_FORUM = 'forum';

    const STATUS_PUBLIC = 'public';
    const STATUS_PRIVATE = 'private';
    const STATUS_PROTECTED = 'protected';

    public function init()
    {
        $this->setTableName('posts');
        $this->setPrimaryKey('post_id');
    }

    public function addPost($sessionKey, $title, $content, $slug, $groupId, $categoryId, $type, $status)
    {
        // Validate session key
        $user = $this->auth($sessionKey);

        // Validate title and content
        $this->_validateTitle($title);
        $this->_validateContent($content);

        // Validate slug
        if($slug) {
            $this->_validateSlug($slug);
        } else {
            $slug = time() . '_' . $user['user_id'];
        }
        if($this->exists(array('slug' => $slug))) {
            throw new InvalidArgumentException('Slug exists: ' . $slug);
        }

        $data = array();
        $data['user_id'] = $user['user_id'];
        $data['title'] = $title;
        $data['slug'] = $slug;
        $data['content'] = $content;

        // Check group and category
        if($groupId > 0) {
            $this->getService('Social.Group.Member')->checkMember($groupId, $user['user_id']);
            $data['group_id'] = $groupId;
            if($categoryId) {
                $this->getService('Social.Post.Category')->checkGroup($categoryId, $groupId);
                $data['category_id'] = $categoryId;
            }
        } else {
            if($categoryId) {
                $this->getService('Social.Post.Category')->checkUser($categoryId, $user['user_id']);
                $data['category_id']= $categoryId;
            }
        }

        // Validate type and status
        if($type) {
            $this->_validateType($type);
        } else {
            $type = self::TYPE_BLOG;
        }
        if($status) {
            $this->_validateStatus($status);
        } else {
            $status = self::STATUS_PUBLIC;
        }

        $data['type'] = $type;
        $data['status'] = $status;
        $data['created_at'] = date('Y-m-d H:i:s');
        return $this->insert($data);
    }

    public function editPost($sessionKey, $postId, $title, $content, $slug, $categoryId, $status)
    {
        // Validate session key
        $user = $this->auth($sessionKey);

        // Validate title, content and slug
        $data = array();
        $this->_validateTitle($title);
        $this->_validateContent($content);
        if($slug) {
            $this->_validateSlug($slug);
            $data['slug'] = $slug;
        }

        // Get post
        $post = $this->getPost($postId);

        // Check permission
        if($user['role_code'] != 'admin' || $post['user_id'] != $user['user_id']) {
            throw new InvalidArgumentException('You have no permission to edit this post');
        }

        // Check categoryId
        if($categoryId) {
            $categoryService = $this->getService('Social.Post.Category');
            if($post['group_id'] > 0) {
                $categoryService->checkGroup($categoryId, $groupId);
            } else {
                $categoryService->checkUser($categoryId, $user['user_id']);
            }
            $data['category_id'] = $categoryId;
        }

        // Validate status
        if($status) {
            $this->_validateStatus($status);
            $data['status'] = $status;
        }

        $data['updated_at'] = date('Y-m-d H:i:s');
        return $this->updateByPrimary($postId, $data);
    }

    public function deletePost($sessionKey, $postId)
    {
        // Validate session key
        $user = $this->auth($sessionKey);

        // Get post
        $post = $this->getPost($postId);

        // Check permission
        if($user['role_code'] != 'admin' || $post['user_id'] != $user['user_id']) {
            throw new InvalidArgumentException('You have no permission to delete this post');
        }

        // Delete post and its comments
        $adapter = $this->getAdapter();
        $adapter->beginTransation();
        try {
            $this->deleteByPrimaryKey($postId);
            $this->getService('Service.Post.Comment')->delete(array('post_id' => $postId));
            $adapter->commit();
        } catch(Exception $e) {
            $adapter->rollBack();
            throw $e;
        }
        return true;
    }

    public function viewPost($sessionKey, $postId, $withComments, $commentCurrentPage, $commentPageSize)
    {
        $post = $this->_getPost($sessionKey, $postId);
        if($post && $withComments) {
            $post['comments'] = $this->_getPostComments($postId, $commentCurrentPage, $commentPageSize);
        }
        return $post;
    }

    private function _getPostComments($postId, $currentPage, $pageSize)
    {
        $commentService = $this->getService('Social.Post.Comment');
        $conditions = array('post_id' => $postId);
        $fields = array(); $order = array();
        $comments = $commentService->getByPage($conditions, $fields, $order, $currentPage, $pageSize);

        // Get users
        $userIds = System_Array::getColumn($comments['record'], 'user_id');
        $users = $this->getService('Social.User')->find(array('user_id' => $userIds), array('user_id', 'username'));

        // Merge user into comments record
        $comments['record'] = System_Array::mergeIn($comments['record'], $users, 'user_id', 'author');
        return $comments;
    }

    private function _getPost($sessionKey, $postId)
    {
         $post = $this->getPost($postId);

        // Public
        if($post['status'] == self::STATUS_PUBLIC) {
            $post['author'] = $this->getService('Social.User')->getUser($post['user_id']);
            return $post;
        }

        // Validate session key
        $user = $this->auth($sessionKey);
        if($post['user_id'] == $user['user_id']) {
            $post['author'] = $user;
            return $post;
        }

        // Private
        if($post['status'] == self::STATUS_PRIVATE) {
            throw new InvalidArgumentException('You have no permission to access this post');
        }

        // Protected
        if($post['status'] == self::STATUS_PROTECTED) {
            if($post['group_id'] > 0) {
                $canAccess = $this->getService('Social.Group.Member')->hasMember($post['group_id'], $user['user_id']);
            } else {
                $canAccess = $this->getService('Social.User.Friend')->hasFriend($post['user_id'], $user['user_id']);
            }

            if($canAccess) {
                $post['author'] = $this->getService('Social.User')->getUser($post['user_id']);
                return $post;
            } else {
                throw new InvalidArgumentException('You have no permission to access this post');
            }
        }

        throw new InvalidArgumentException('Unknown error');
    }

    public function listPost($type, $userId, $groupId, $categoryId, $currentPage, $pageSize)
    {
        $type = $this->_checkType($type);
        $conditions = array('group_id' => $groupId, 'type' => $type);
        if($userId) {
            $conditions['user_id'] = $userId;
        }
        if($groupId) {
            $conditions['group_id'] = $groupId;
        }
        if($categoryId) {
            $conditions['category_id'] = $categoryId;
        }

        $order = null;
        $fields = array('post_id', 'user_id', 'title', 'created_at');
        $posts = $this->findByPage($conditions, $fields, $order, $currentPage, $pageSize);

        // Get users
        $userIds = System_Array::getColumn($posts['record'], 'user_id');
        $users = $this->getService('Social.User')->find(array('user_id' => $userIds), array('user_id', 'username'));

        // Merge user into posts record
        $posts['record'] = System_Array::mergeIn($posts['record'], $users, 'user_id', 'author');
        return $posts;
    }

    public function getPost($postId)
    {
        $post = $this->getByPrimary($postId);
        if(empty($post)) {
            throw new InvalidArgumentException('Post Not found');
        }
        return $post;
    }

    private function _validateTitle($title)
    {
        $this->validate($title, 'title', 'required', null, 'Title required');
        $this->validate($title, 'title', 'minLength', 3, 'Title must be 3 characters more.');
        $this->validate($title, 'title', 'maxLength', 255, 'Title must be 255 characters or less.');
    }

    private function _validateSlug($slug)
    {
        $this->validate($slug, 'slug', 'required', null, 'Slug required');
        $this->validate($slug, 'slug', 'minLength', 3, 'Slug must be 3 characters more.');
        $this->validate($slug, 'slug', 'maxLength', 255, 'Slug must be 255 characters or less.');
    }

    private function _validateContent($content)
    {
        $this->validate($content, 'content', 'required', null, 'Content required');
        $this->validate($content, 'content', 'minLength', 3, 'Content must be 3 characters more.');
        $this->validate($content, 'content', 'maxLength', 50000, 'Content must be 50000 characters or less.');
    }

    private function _validateType($type)
    {
        $types = array(self::TYPE_BLOG, self::TYPE_FORUM, self::TYPE_WIKI);
        if(!in_array($type, $types)) {
            throw new InvalidArgumentException(sprintf('This type: %s was not supported', $type));
        }
        return true;
    }

    private function _validateStatus($status)
    {
        $statuses = array(self::STATUS_PUBLIC, self::STATUS_PRIVATE, self::STATUS_PROTECTED);
        if(!in_array($status, $statuses)) {
            throw new InvalidArgumentException(sprintf('This status: %s was not supported', $status));
        }
        return true;
    }

}
